import XLSX from 'xlsx'
import _ from 'lodash'
import ExcelImportDialog from "./renderers/excelImportDialog";
import * as path from "path";
import {clearLoading, loading} from "./renderers";
// @ts-ignore
// import XLSX_EXPORT from 'xlsx_export';
// @ts-ignore
const XLSX_EXPORT = window.LAY_EXCEL

export {XLSX};
export {XLSX_EXPORT}

export function readExcel(file: File): Promise<any> {
    return new Promise((resolve, reject) => {
        var reader = new FileReader();
        reader.onload = function (e) {
            if (e.target) {
                const data = e.target.result;
                const workbook = XLSX.read(data, {type: 'binary'});
                resolve(workbook);
            } else {
                reject("文件读取失败！！！")
            }
        };
        try {
            reader.readAsBinaryString(file);
        } catch (e) {
            reject(e)
        }
    })
}

export declare interface ErrorMsgDataItem {
    /** 错误id 由allData 的__importID__字段值 + "_" + field的值 表示哪一行的哪一列出错，行校验错误时候直接为allData 的__importID__ + "" */
    errorId: string
    /** 对应数据行 allData 的__importID__字段值 */
    importID: number
    /** 字段 */
    field: string
    /** 字段值 */
    title: string
    /** 字段值 */
    value: string
    /** 错误信息 */
    errormessage: string
}

export declare interface ImportResult {
    /** 所有导入的数据，如果有字典会格式化到字典，格式化错误的保持原始值 */
    allData: any[]
    /** 导入正确的数据 */
    okData: any[]
    /** 导入错误的数据 */
    errorData: any[]
    /** 导入错误数据的错误明细 */
    errorMsgData: ErrorMsgDataItem[]
}

/** 定义参数列 */
export declare interface Field {
    /** 字段 */
    field: string
    /** 字段名 */
    title: string
    /** 校验方法，校验通过返回true， 否则返回错误信息 会记录到错误列表里面 errorMsgData */
    validate?: ((v: ValidateObject) => true | string) | string
    /** 格式化，表格显示 兼容默认弹出框表格的formatter */
    formatter?: ((v: any) => any) | string
    /** 导入格式化 返回null或者undefined 表示格式化错误，会记录到错误列表里面 errorMsgData */
    importFormatter?: ((v: ValidateObject) => null | undefined | string | number) | string
    /** 字典， 参与数据校验和表格显示格式化， 校验不通过的，会记录到错误列表里面 errorMsgData 同时兼容默认弹出框表格的字典 */
    data?: any | { id: string | number, text: string }
}

/** 格式化及校验的参数 */
export declare interface ValidateObject {
    field: Field
    ov: any,
    nv: any,
    rowIndex: number,
    data: any,
    rowDatas: any[],
}

/** 行数据校验的参数 */
export declare interface RowValidateObject {
    fields: Field[],
    data: any,
    rowIndex: number,
    rowDatas: any[]
}

/**
 * 定义切口，可以在外部修改数据
 */
export declare type AfterClientValidate =
    ((importResult: ImportResult, resolve: (value?: (ImportResult | PromiseLike<ImportResult> | undefined)) => void) => ImportResult)
    | undefined

export function readExcelWithFieldSet(file: File,
                                      fieldSet: any[],
                                      dataStartRow: number = 2,
                                      titleRowNumber: number = 1,
                                      rowValidate: ((rv: RowValidateObject) => true | string) | undefined = undefined,
                                      otherValidate: AfterClientValidate = undefined,
                                      fieldValidate: ((fields: Field[], columnTitles: string[]) => boolean) | undefined = undefined): Promise<ImportResult> {
    return new Promise<ImportResult>((resolve, reject) => {
        setTimeout(()=>{
            readExcel(file).then(workbook => {
                const sheetNames = workbook.SheetNames; // 工作表名称集合
                const worksheet = workbook.Sheets[sheetNames[0]]; // 这里我们只读取第一张sheet
                const titleRowValue: any[] = [];
                const titleRowKey: any[] = [];
                let needLoop: boolean = false; // 是否需要迭代
                let fields: any[] = [];
                if (worksheet && worksheet["!ref"]) {
                    const t = worksheet["!ref"];
                    const tempArr = t.split(':');
                    if (tempArr.length >= 2) {
                        let firstRowNumber = tempArr[0].replace(/[^0-9]/ig, "");

                        // 选取title的行，删除之前的行
                        if (titleRowNumber > firstRowNumber) {
                            for (let i = firstRowNumber; i < titleRowNumber; i++) {
                                for (const key in worksheet) {
                                    if (key.endsWith(i) && key.replace(/[^0-9]/ig, "") === i) {
                                        delete worksheet[key];
                                    }
                                }
                            }
                            const fc = tempArr[0].replace(/[^a-z]/ig, "");
                            firstRowNumber = titleRowNumber + '';
                            worksheet["!ref"] = fc+firstRowNumber + ":" + tempArr[1]
                        }
                        const lastRowNumber = tempArr[1].replace(/[^0-9]/ig, "");
                        // const firstColNumber = tempArr[0].split(''+firstRowNumber)[0];
                        // const lastColNumber = tempArr[1].split(''+lastRowNumber)[0];
                        for (const key in worksheet) {
                            if (key.endsWith(firstRowNumber) && key.replace(/[^0-9]/ig, "") === firstRowNumber) {
                                titleRowKey.push(key)
                                titleRowValue.push(worksheet[key].v)
                            }
                        }
                        if (fieldSet && fieldSet.length > 0) {
                            let length = fieldSet.length;
                            length = length <= titleRowKey.length ? length : titleRowKey.length;
                            for (let i = 0; i < length; i++) {
                                const vk = titleRowKey[i];
                                const item = fieldSet[i];
                                const field: any = {}
                                if (typeof item === 'string') {
                                    field.field = item;
                                    field.title = worksheet[vk].v;
                                    worksheet[vk] = {t: "s", v: item, h: item, w: item}
                                } else if (typeof item.field === "string") {
                                    field.field = item.field;
                                    field.title = worksheet[vk].v;
                                    worksheet[vk] = {t: "s", v: item.field, h: item.field, w: item.field};
                                    if (typeof item.validate === "function") {
                                        field.validate = item.validate
                                        needLoop = true;
                                    }
                                    if (typeof item.importFormatter === "function") {
                                        field.importFormatter = item.importFormatter
                                        needLoop = true;
                                    }
                                    if ((item.data instanceof Array && item.data.length > 0)
                                        || _.isPlainObject(item.data) && Object.keys(item.data).length > 0) {
                                        field.data = item.data
                                        needLoop = true;
                                    }
                                }
                                fields.push(field)
                            }
                        }
                    }
                }

                if (fieldValidate && typeof fieldValidate === "function") {
                    if (fieldValidate(fields, titleRowValue) !== true) {
                        reject("fields validate error");
                        return
                    }
                }

                const allData = XLSX.utils.sheet_to_json(worksheet);
                let okData: any[] = [], errorData: any[] = [], errorMsgData: ErrorMsgDataItem[] = [];
                const needItemLoop = needLoop;
                if (rowValidate && typeof rowValidate === "function") {
                    needLoop = true;
                }
                if (needLoop === true) {
                    for (let index = 0; index < allData.length; index++) {
                        const row: any = allData[index];
                        const rowNumber = dataStartRow + index;
                        row.__importID__ = rowNumber
                        let isRowOk: boolean = true;
                        if (needItemLoop) {
                            for (let num = 0; num < fields.length; num++) {
                                const field = fields[num];
                                const ov = row[field.field];
                                let nv: any = ov;

                                let hasError: boolean = false;
                                const rowNumber = dataStartRow + index;
                                const ei: ErrorMsgDataItem = {
                                    errorId: rowNumber + '_' + field.field,
                                    importID: rowNumber,
                                    field: field.field,
                                    title: field.title,
                                    value: ov,
                                    errormessage: ""
                                }

                                // 格式化
                                if (field.data instanceof Array) {
                                    nv = undefined;
                                    ei.errormessage = "字典匹配失败"
                                    _.forEach(field.data, (v, index) => {
                                        if (v.text === ov) {
                                            nv = v.id;
                                            return
                                        }
                                    })
                                } else if (_.isPlainObject(field.data)) {
                                    nv = undefined;
                                    ei.errormessage = "字典匹配失败"
                                    _.forEach(field.data, (value, key) => {
                                        if (value === ov) {
                                            nv = key;
                                            return
                                        }
                                    })
                                }

                                if (field.importFormatter) {
                                    const vdata: ValidateObject = {
                                        field,
                                        ov,
                                        nv,
                                        rowIndex: index,
                                        data: row,
                                        rowDatas: allData
                                    }
                                    nv = undefined;
                                    ei.errormessage = "格式化失败";
                                    nv = field.importFormatter(vdata)
                                }

                                if (nv === undefined || nv === null) {
                                    hasError = true;
                                } else {
                                    row[field.field] = nv;
                                    ei.errormessage = "";
                                }

                                // 校验
                                if (field.validate) {
                                    const vdata: ValidateObject = {
                                        field,
                                        ov,
                                        nv,
                                        rowIndex: index,
                                        data: row,
                                        rowDatas: allData
                                    }
                                    const errormessage: true | string = field.validate(vdata);
                                    if (errormessage === true) {

                                    } else {
                                        hasError = true;
                                        ei.errormessage = ei.errormessage + "/" + errormessage;
                                    }
                                }
                                if (hasError === true) {
                                    isRowOk = false;
                                    errorMsgData.push(ei);
                                }
                            }
                        }
                        if (rowValidate && typeof rowValidate === "function") {
                            const errormessage: true | string = rowValidate({
                                fields,
                                data: row,
                                rowIndex: index,
                                rowDatas: allData
                            })
                            if (errormessage === true) {

                            } else {
                                isRowOk = false;

                                const ei: ErrorMsgDataItem = {
                                    errorId: rowNumber + '',
                                    field: "row",
                                    title: "数据行",
                                    value: "当前行的数据",
                                    importID: rowNumber,
                                    errormessage
                                }
                                errorMsgData.push(ei);
                            }
                        }
                        row.__hasError__ = !isRowOk
                        if (isRowOk === true) {
                            okData.push(row)
                        } else {
                            errorData.push(row)
                        }
                    }

                } else {
                    // 添加数据的唯一标识
                    for (let index = 0; index < allData.length; index++) {
                        const row: any = allData[index];
                        const rowNumber = dataStartRow + index;
                        row.__importID__ = rowNumber;
                        row.__hasError__ = false
                    }
                    okData = allData;
                }
                if (otherValidate && typeof otherValidate === "function") {
                    otherValidate({allData, okData, errorData, errorMsgData}, resolve);
                } else {
                    resolve({allData, okData, errorData, errorMsgData});
                }
                clearLoading()
            })
        }, 50);
    })
}

export declare interface ImportExcelOption {
    // 字段设置
    fields: Field[],
    // 字段校验方法
    fieldValidate?: ((fields: Field[], columnTitles: string[]) => boolean) | undefined,
    // 数据行校验方法
    rowValidate?: ((rv: RowValidateObject) => true | string) | undefined,
    // 客户端校验完成后调用
    afterClientValidate?: AfterClientValidate,
    // Excel起始数据行的的行号 默认给定为2
    dataStartRow?: number,
    // Excel标题行的的行号 默认给定为1
    titleRowNumber?: number,
    // 导入完成后调用 回传结果数据
    onImportAfter?: (result: ImportResult) => {}
    // 隐藏所有数据表格， 默认false
    hidAllDataGrid?: boolean,
    // 隐藏正确数据表格 默认 false
    hidOKDataGrid?: boolean,
    // 隐藏错误数据的表格 默认false
    hidErrorDataGrid?: boolean,
    // 隐藏错误信息表格 默认false
    hidErrorMsgGrid?: boolean,
    // 所有数据表格的分页大小， 0 为不分页
    dataGridPageSize?: 0 | 10 | 20 | 50 | 100 | 200 | 500 | 1000,
    // 错误信息表格的分页大小， 0 为不分页
    errorMsgGridPageSize?: 0 | 10 | 20 | 50 | 100 | 200 | 500 | 1000,
    // 导入模版的名称
    templateName?: string,
    // 导入模板下载地址
    dowLoadUrl?: string,
    // 标题
    title?: string,
    // 弹窗高
    height?: string,
    // 弹窗宽
    width?: string,
    // 默认显示的表格
    defaultShowData?: "allData" | "okData" | "errorData",
    // 自定义错误信息表格的列宽
    errMsgGridColWidths?: number[],
    // 外部定义的控件
    toolBar?: any[],
    // 关闭回调
    onClose?: string | (() => void)
}

/**
 * 打开excel导入的方法
 * @param option 参数配置
 * @param sender
 */
export function importExcel(option: ImportExcelOption, sender: any): ExcelImportDialog {
    const dialog = new ExcelImportDialog({option})
    dialog.showDialog(sender);
    return dialog
}